package entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Observer;

public class EEntity {

	private HashMap<String,ENode> nodes = new LinkedHashMap<String,ENode>();
	public enum Datatypes
	{
		q1616, sq1616,uint32_t,int32_t,bool,eplreg, datedatadef, timedatadef,
		gpsdegdef, defaulttype;
	    
	    public static Datatypes toDatatype(String datatype)
	    {
	        try {
	            return valueOf(datatype.toLowerCase().replace(".", ""));
	        } 
	        catch (Exception ex) {
	            return defaulttype;
	        }
	    }
	    
	    public static boolean contains(String test) {

	        for (Datatypes d : Datatypes.values()) {
	            if (d.name().equalsIgnoreCase(test)) {
	                return true;
	            }
	        }

	        return false;
	    }
	}
	
	public void addNode(String name, int nodeID, int objectIndex, 
			int BRAMOffset) {
		nodes.put(name, new ENode(name, nodeID, objectIndex, BRAMOffset));
	}
	
	public void addRegister(String nodeName, String registerName, 
			String dataType, int address, String description, String unit, double min ,double max) {
		switch (Datatypes.toDatatype(dataType)) {
			case q1616:
				nodes.get(nodeName).addRegister(new EQ1616Register(registerName,
						address, dataType, description, unit, min, max));
				break;
			case sq1616:
				nodes.get(nodeName).addRegister(new ESQ1616Register(registerName,
						address, dataType, description, unit, min, max));
				break;
			case uint32_t:
				nodes.get(nodeName).addRegister(new EuintRegister(registerName,
						address, dataType, description, unit, min, max));
				break;
			case int32_t:
				nodes.get(nodeName).addRegister(new EintRegister(registerName,
						address, dataType, description, unit, min, max));
				break;
			case bool:
				nodes.get(nodeName).addRegister(new EboolRegister(registerName,
						address, dataType, description, unit));
				break;
			case eplreg:
				nodes.get(nodeName).addRegister(new EEPLRegRegister(registerName,
						address, dataType, description, unit));
				break;
			case datedatadef:
				nodes.get(nodeName).addRegister(new EDateRegister(registerName,
						address, dataType, description, unit));
				break;
			case timedatadef:
				nodes.get(nodeName).addRegister(new ETimeRegister(registerName,
						address, dataType, description, unit));
				break;
			case gpsdegdef:
				nodes.get(nodeName).addRegister(new ECoordinateRegister(registerName,
						address, dataType, description, unit,min,max));
				break;
			case defaulttype:
			default:
				nodes.get(nodeName).addRegister(new EStringRegister(registerName,
						address, dataType, description, unit));
				break;
		}
	}
	
	public void addRegister(String nodeName, String registerName, 
			String dataType, int address, String description, String unit,
			EPattern[] patterns) {
		nodes.get(nodeName).addRegister(new EPatternRegister(registerName, address, 
				dataType, description, unit,patterns));
	}
	
	public HashMap<String,ERegister> getRegisters(String node) {
		return nodes.get(node).getRegisters();
	}
	
	public String[] getPlotableRegisters() {
		ArrayList<String> plotableRegisters = new ArrayList<String>();
		Iterator<String> nodeKeyset = nodes.keySet().iterator();
		HashMap<String,ERegister> currentNodeRegisters;
		
		while (nodeKeyset.hasNext()) {
			currentNodeRegisters = nodes.get(nodeKeyset.next()).getRegisters();
			Iterator<String> registerKeyset = 
				currentNodeRegisters.keySet().iterator();
			ERegister currentRegister;
			
			while (registerKeyset.hasNext()) {
				currentRegister = 
					currentNodeRegisters.get(registerKeyset.next());
				if (currentRegister.isPlotable()) {
					plotableRegisters.add(currentRegister.getName());
				}
			}
		}
		
		String[] registers = new String[plotableRegisters.size()];
		plotableRegisters.toArray(registers);
		return registers;
	}
	
	public String getRegisterUnit(String register) {
		String registerUnit = null;
		Iterator<String> nodeKeyset = nodes.keySet().iterator();
		HashMap<String,ERegister> currentNodeRegisters;
		
		while (nodeKeyset.hasNext() && registerUnit == null) {
			currentNodeRegisters = nodes.get(nodeKeyset.next()).getRegisters();
			Iterator<String> registerKeyset = 
				currentNodeRegisters.keySet().iterator();
			ERegister currentRegister;
			
			while (registerKeyset.hasNext() && registerUnit == null) {
				currentRegister = 
					currentNodeRegisters.get(registerKeyset.next());
				if (currentRegister.getName().equalsIgnoreCase(register)) {
					registerUnit = currentRegister.getUnit();
				}
			}
		}
		
		return registerUnit;
	}
	
	public ERegister getRegister(String registerName) {
		String nodeName = null;
		Iterator<String> nodeKeyset = nodes.keySet().iterator();
		HashMap<String,ERegister> currentNodeRegisters;
		ENode currentNode;
		
		while (nodeKeyset.hasNext() && nodeName == null) {
			currentNode = nodes.get(nodeKeyset.next());
			currentNodeRegisters = currentNode.getRegisters();
			Iterator<String> registerKeyset = 
				currentNodeRegisters.keySet().iterator();
			ERegister currentRegister;
			
			while (registerKeyset.hasNext() && nodeName == null) {
				currentRegister = 
					currentNodeRegisters.get(registerKeyset.next());
				if (currentRegister.getName().equalsIgnoreCase(registerName)) {
					return currentRegister;
				}
			}
		}
		
		return null;
	}
	
	public double[] getRegisterBoundaries(String register) {
		double[] boundary = {0.,0.};
		try {
			boundary = ((ENumberRegister) this.getRegister(register)).getBoundary();
		} catch (Exception e) {}
		
		return boundary;
	}
	
	public String[] getNodeList() {
		String[] nodeArray = new String[nodes.size()];
		
		nodes.keySet().toArray(nodeArray);
		return nodeArray;
	}
	
	public void addObserver(String node, String register, Observer observer) {
		nodes.get(node).addObserver(register,observer);
	}
	
	public void removeObserver(String node, String register, Observer observer) {
		nodes.get(node).removeObserver(register,observer);
	}
	
	public void addObserver(String register, Observer observer) {
		String nodeName = null;
		Iterator<String> nodeKeyset = nodes.keySet().iterator();
		HashMap<String,ERegister> currentNodeRegisters;
		ENode currentNode;
		
		while (nodeKeyset.hasNext() && nodeName == null) {
			currentNode = nodes.get(nodeKeyset.next());
			currentNodeRegisters = currentNode.getRegisters();
			Iterator<String> registerKeyset = 
				currentNodeRegisters.keySet().iterator();
			ERegister currentRegister;
			
			while (registerKeyset.hasNext() && nodeName == null) {
				currentRegister = 
					currentNodeRegisters.get(registerKeyset.next());
				if (currentRegister.getName().equalsIgnoreCase(register)) {
					nodeName = currentNode.getName();
				}
			}
		}

		addObserver(nodeName,register,observer);
	}
	
	public void removeObserver(String register, Observer observer) {
		String nodeName = null;
		Iterator<String> nodeKeyset = nodes.keySet().iterator();
		HashMap<String,ERegister> currentNodeRegisters;
		ENode currentNode;
		
		while (nodeKeyset.hasNext() && nodeName == null) {
			currentNode = nodes.get(nodeKeyset.next());
			currentNodeRegisters = currentNode.getRegisters();
			Iterator<String> registerKeyset = 
				currentNodeRegisters.keySet().iterator();
			ERegister currentRegister;
			
			while (registerKeyset.hasNext() && nodeName == null) {
				currentRegister = 
					currentNodeRegisters.get(registerKeyset.next());
				if (currentRegister.getName().equalsIgnoreCase(register)) {
					nodeName = currentNode.getName();
				}
			}
		}

		removeObserver(nodeName,register,observer);
	}
	
	public void updateRegisterData(byte[] data) {
		Iterator<String> nodeKeyset = nodes.keySet().iterator();
		HashMap<String,ERegister> currentNodeRegisters;
		int address;
		
		while (nodeKeyset.hasNext()) {
			currentNodeRegisters = nodes.get(nodeKeyset.next()).getRegisters();
			Iterator<String> registerKeyset = 
				currentNodeRegisters.keySet().iterator();
			ERegister currentRegister;
			
			while (registerKeyset.hasNext()) {
				currentRegister = 
					currentNodeRegisters.get(registerKeyset.next());
				address = currentRegister.getAddress();
				currentRegister.setValue(data[address],data[address+1],data[address+2],data[address+3]);
			}
		}
	}
	
	public String getRegisterName(String node, String startsWith) {
		return nodes.get(node).getRegisterName(startsWith);
	}
}
